package tool.analysis;

import java.util.Map;
import java.util.TreeMap;

import tool.csv.CSVCompatible;
import tool.csv.FileWriter;
import tool.data.FieldRecord;
import tool.data.ParameterRecord;
import tool.data.ResultStore;

	/**
	 * @author jwb09119
	 * @date 2013/11/25
	 * 
	 * The analyser looks at the use of all types in an application and logs where those types are
	 * interface types.
	 */

public class InterfaceUseAsTypeAnalyser extends CSVCompatible implements IVariableAndParameterAnalyser {

	private ResultStore results;
	private Map<String, Integer> methodVarNotInterface;
	private Map<String, Integer> methodVarIsInterface;
	private Map<String, Integer> classVarNotInterface;
	private Map<String, Integer> classVarIsInterface;
	private Map<String, Integer> methodParamIsInterface;
	private Map<String, Integer> methodParamNotInterface;
	
	public InterfaceUseAsTypeAnalyser(FileWriter writer, ResultStore log) {
			
		super(writer);
		results = log;
		methodVarNotInterface = new TreeMap<>();
		methodVarIsInterface = new TreeMap<>();
		classVarNotInterface = new TreeMap<>();
		classVarIsInterface = new TreeMap<>();
		methodParamIsInterface = new TreeMap<>();
		methodParamNotInterface = new TreeMap<>();
		
		results.visitVariablesAndParameters(this);
		
	}

	
	/**
	 * Check if a specific interface is known to this analyser
	 * @param interfaceName - a String representing a unique interface name
	 * @return true if this analyser has encountered the interface, otherwise false
	 */
    public boolean containsInterface (String interfaceName) {

        boolean reply = false;
        
        for (String key : methodVarIsInterface.keySet()) {
            
            if (key.equals(interfaceName)) {
                reply = true;
                break;
            }
            
        }
        
        if (!reply) {
            
            for (String key : classVarIsInterface.keySet()) {
                
                if (key.equals(interfaceName)) {
                    reply = true;
                    break;  
                }
                
            }
            
        }
        
        if (!reply) {
            
            for (String key : methodParamIsInterface.keySet()) {
                
                if (key.equals(interfaceName)) {
                    reply = true;
                    break;
                }
                
            }
            
        }
        
        
        return reply;
        
    }

	
    /**
     * Gets the number of times a specific interface is used as a class variable in the code base
     * examined by this analyser
     * @param interfaceName - String - a unique identifier for an interface type
     * @return int - the number of times the interface represented by the String interfaceName
     *               used.  Note that this method assumes that the interface being looked for exists
     * @see containsInterface (String interfaceName) for an interface existence check.
     */
    public int getClassVarCountForInterface (String interfaceName) {

        int reply = 0;
        
        if (classVarIsInterface.containsKey(interfaceName)) {
            reply = classVarIsInterface.get(interfaceName);
        }
        
        return reply;
        
    }


    /**
     * Gets the number of times a specific interface is used as a method parameter in the code base
     * examined by this analyser
     * @param interfaceName - String - a unique identifier for an interface type
     * @return int - the number of times the interface represented by the String interfaceName
     *               used.  Note that this method assumes that the interface being looked for exists
     * @see containsInterface (String interfaceName) for an interface existence check.
     */
    public int getMethodParamCountForInterface (String interfaceName) {

        int reply = 0;

        if (methodParamIsInterface.containsKey(interfaceName)) {
            reply = methodParamIsInterface.get(interfaceName);
        }
        
        return reply;
        
    }


    /**
     * Gets the number of times a specific interface is used as a local variable in the code base
     * examined by this analyser
     * @param interfaceName - String - a unique identifier for an interface type
     * @return int - the number of times the interface represented by the String interfaceName
     *               used.  Note that this method assumes that the interface being looked for exists
     * @see containsInterface (String interfaceName) for an interface existence check.
     */
    public int getLocalVarCountForInterface (String interfaceName) {

        int reply = 0;
        
        if (methodVarIsInterface.containsKey(interfaceName)) {
            reply = methodVarIsInterface.get(interfaceName);
        }
        
        return reply;
        
    }
    
	
	@Override
	public void visit(FieldRecord fr) {
		
		String type  = fr.getFullTypeName();
		if (results.checkTypeIsInterface(type)) {
			
			if (fr.isClassVariable()) {
				
				recordClassVarIsInterface(type);
				
			} else {
				
				recordMethodVarIsInterface(type);
				
			}
			
		} else {
			
			if (fr.isClassVariable()) {
				
				recordClassVarIsNotInterface(type);
				
			} else {
				
				recordMethodVarIsNotInterface(type);
				
			}
			
		}
		
	}

	
	@Override
	public void visit(ParameterRecord pr) {

		String type  = pr.getFullTypeName();
		if (results.checkTypeIsInterface(type)) {
				
			recordMethodParamIsInterface(type);
				
		} else {
			
			recordMethodParamNotInterface(type);
			
		}

	}


	private void recordMethodVarIsNotInterface(String fulltypeName) {
		
		int newValue = 1;
		
		if (methodVarNotInterface.containsKey(fulltypeName)) {
			
			newValue = methodVarNotInterface.get(fulltypeName)+1;
			
		}
		
		methodVarNotInterface.put(fulltypeName, newValue);
		
	}


	private void recordClassVarIsNotInterface(String fulltypeName) {
		
		int newValue = 1;
		
		if (classVarNotInterface.containsKey(fulltypeName)) {
			
			newValue = classVarNotInterface.get(fulltypeName)+1;
				
		}
		
		classVarNotInterface.put(fulltypeName, newValue);
		
	}


	private void recordMethodVarIsInterface(String fulltypeName) {

		int newValue = 1;
		
		if (methodVarIsInterface.containsKey(fulltypeName)) {
			
			newValue = methodVarIsInterface.get(fulltypeName)+1;
			
		}
		
		methodVarIsInterface.put(fulltypeName, newValue);
		
	}


	private void recordClassVarIsInterface(String fulltypeName) {

		int newValue = 1;
		
		if (classVarIsInterface.containsKey(fulltypeName)) {
			
			newValue = classVarIsInterface.get(fulltypeName)+1;
			
		}
		
		classVarIsInterface.put(fulltypeName, newValue);
		
	}
	
	
	private void recordMethodParamNotInterface(String fulltypeName) {

		int newValue = 1;
		
		if (methodParamNotInterface.containsKey(fulltypeName)) {
			
			newValue = methodParamNotInterface.get(fulltypeName)+1;
			
		}
		
		methodParamNotInterface.put(fulltypeName, newValue);
		
	}


	private void recordMethodParamIsInterface(String fulltypeName) {
		
		int newValue = 1;
		
		if (methodParamIsInterface.containsKey(fulltypeName)) {
			
			newValue = methodParamIsInterface.get(fulltypeName)+1;
			
		}
		
		methodParamIsInterface.put(fulltypeName, newValue);
		
	}
	
	
    public void printSummary () {

        System.out.println("\nInterface Use Analysis:");
        System.out.println("\t Class Variables: " + classVarIsInterface.size() + " of " + 
                    (classVarIsInterface.size() + classVarNotInterface.size()) + 
                    " types are interfaces");
        
        System.out.println("\t Method Parameters: " + methodParamIsInterface.size() + " of " +
             (methodParamIsInterface.size() + methodParamNotInterface.size()) + 
             " types are interfaces");
       
        System.out.println("\t Local Method Variables: " + methodVarIsInterface.size() + " of " +
                (methodVarIsInterface.size() + methodVarNotInterface.size()) + 
                " types are interfaces");
        
    }
	
	
	/***************
	 * CSV Methods *
	 ***************/

	@Override
	protected String getExportHeader() {
		
		return "Information about the use proportion of interfaces an non interfaces in class "
				+ "variables, method parameters and local method variables";
		
	}


	@Override
	protected void writeStateToCSV(FileWriter writer) {
		
		writer.writeComment("typeName", "scope", "IsInterface", "noTimesUsed");
		
		// Count totals in each map and ratios between them
		// List all records in a away that makes it easy to see what is in each section AND ALSO
		// that reveals if any problems have occurred resolving Types.

		writeMapToLines (methodVarNotInterface, "METHODVAR", false, writer);
		writeMapToLines (methodVarIsInterface, "METHODVAR", true, writer);
		writeMapToLines (classVarNotInterface, "CLASS", false, writer);
		writeMapToLines (classVarIsInterface, "CLASS", true, writer);
		writeMapToLines (methodParamNotInterface, "METHODPARAM", false, writer);
		writeMapToLines (methodParamIsInterface, "METHODPARAM", true, writer);
		
	}
	

	private void writeMapToLines (Map<String, Integer> map, String scope, boolean isInterface, FileWriter writer) {
		
		for (String key : map.keySet()) {
			
			writer.writeComment(key, scope, String.valueOf(isInterface), String.valueOf(map.get(key)));
			
		}
		
	}


}
